package cn.edu.csust.coursecard.service.impl;


import cn.edu.csust.coursecard.common.CodeEnum;
import cn.edu.csust.coursecard.dao.PostMapper;
import cn.edu.csust.coursecard.dao.dto.*;
import cn.edu.csust.coursecard.dao.entity.Post;
import cn.edu.csust.coursecard.dao.entity.PostImage;
import cn.edu.csust.coursecard.dao.entity.PostSearchVo;
import cn.edu.csust.coursecard.dao.entity.StuInfo;
import cn.edu.csust.coursecard.exception.BaseException;
import cn.edu.csust.coursecard.service.ICommentService;
import cn.edu.csust.coursecard.service.IPostService;
import cn.edu.csust.coursecard.service.PostReportService;
import cn.edu.csust.coursecard.service.StuInfoService;
import cn.edu.csust.coursecard.utils.SensitiveFilter;
import cn.edu.csust.coursecard.utils.storage.FileUtils;
import cn.edu.csust.coursecard.utils.storage.StorageUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service("postService")
public class PostServiceImpl implements IPostService {

    private static final Logger LOGGER = LoggerFactory.getLogger(PostServiceImpl.class);

    @Autowired
    private PostMapper postMapper;

    @Resource
    private StorageUtils fileUtil;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private ICommentService commentService;

    @Autowired
    private PostReportService postReportService;

    @Autowired
    private SensitiveFilter sensitiveFilter;

    @Autowired
    private StuInfoService stuInfoService;

    public final static String DEFAULT_MAN_AVATAR1 = "man1.jpg";

    public final static String DEFAULT_MAN_AVATAR2 = "man2.jpg";

    public final static String DEFAULT_WOMAN_AVATAR1 = "woman1.jpg";

    public final static String DEFAULT_WOMAN_AVATAR2 = "woman2.jpg";

    public static long defaultAvatarIndex = Long.MIN_VALUE;


    @Override
    @Transactional
    public IndexPost write(Post post, List<MultipartFile> images) {
        //参数校验
        if (StringUtils.isEmpty(post.getContent())
                || post.getThemeId() == null || post.getThemeId() <= 0) {
            throw new BaseException(CodeEnum.PARAM_ERROR.getCode(), "博客信息为空错误");
        }
        if (!CollectionUtils.isEmpty(images) && images.size() > 3) {
            throw new BaseException(CodeEnum.PARAM_ERROR.getCode(), "图片数量超过限制");
        }
        Boolean hasKey = redisTemplate.opsForHash().hasKey(RedisKeyConstant.THEME, post.getThemeId());
        if (!hasKey) {
            throw new BaseException(CodeEnum.PARAM_ERROR.getCode(), "标签不存在错误");
        }
        post.setContent(sensitiveFilter.filter(post.getContent()));
        //补充参数
        Date date = new Date();
        post.setIsValid(true).setLikeNum(0).setCommentNum(0).setCreateTime(date).setUpdateTime(date);
        int rows = postMapper.insertSelective(post);
        if (rows != 1) {
            throw new BaseException(CodeEnum.REQUEST_FAILED.getCode(), "发帖失败");
        }
        Integer postId = post.getId();
        redisTemplate.opsForValue().set(RedisKeyConstant.LIKE_NUM + postId, 0, 7, TimeUnit.DAYS);

        //添加博客与主题
//        postMapper.insertPostTheme(postId,post.getThemeIds());

        //添加图片
        List<String> urls = new ArrayList<>();
        if (!CollectionUtils.isEmpty(images)) {
            for (MultipartFile image : images) {
                String fileName = "post/" + FileUtils.createFileName(image);
                fileUtil.upload(image, fileName);
                urls.add(fileUtil.getUrl() + fileName);
            }

            try {
                postMapper.insertPostImage(postId, urls);
            } catch (Exception e) {
                for (String url : urls) {
                    int index = url.lastIndexOf("/");
                    fileUtil.delete("post/" + url.substring(index + 1));
                }
                throw new BaseException(CodeEnum.PICTURE_UPLOAD_ERROR.getCode(), "服务端错误");
            }
        }
        //前端所需要的post数据
        StuInfo stuInfo = stuInfoService.getStuInfoFromDB(post.getUserId(), post.getUserId(), null);
        UserInfo userInfo = new UserInfo(stuInfo.getId(), stuInfo.getAvatar(), stuInfo.getUsername());
        setAnonymous(userInfo, stuInfo.getSex(), post.getIsAnonymous());
        return new IndexPost(postId, userInfo, post.getContent(), post.getLikeNum(), false, false, post.getCommentNum(), post.getIsAnonymous(), urls, post.getCreateTime());
    }

//    /**
//     * 获取标签id
//     */
//    private Set<Integer> getThemeIds(){
//        Set<Object> keys = redisTemplate.opsForHash().keys(RedisKeyConstant.THEME);
//        if(CollectionUtils.isEmpty(keys)){
//            Map<Integer,Object> map = getThemeMapFromDataBase();
//            return map.keySet();
//        }
//        return keys.stream().map(key -> (Integer)key).collect(Collectors.toSet());
//    }

    /**
     * 从数据库中获取标签信息
     */
    private List<Theme> getThemeMapFromDataBase() {
        List<Theme> themeList = postMapper.selectThemes();
        Map<Integer, Object> map = new HashMap<>();
        for (Theme theme : themeList) {
            map.put(theme.getId(), theme);
        }
        redisTemplate.opsForHash().putAll(RedisKeyConstant.THEME, map);
        redisTemplate.expire(RedisKeyConstant.THEME, 7, TimeUnit.DAYS);
        return themeList;
    }

    @Override
    public List<Theme> getTheme() {
        List<Object> values = redisTemplate.opsForHash().values(RedisKeyConstant.THEME);
        if (CollectionUtils.isEmpty(values)) {
            return getThemeMapFromDataBase();
        }
        return values.stream().map(obj -> (Theme) obj).collect(Collectors.toList());
    }

    @Override
    public void addTheme(Theme theme) {
        if (StringUtils.isEmpty(theme.getThemeName())) {
            throw new BaseException(CodeEnum.PARAM_ERROR.getCode(), "标签内容不能为空");
        }
        theme.setThemeName(sensitiveFilter.filter(theme.getThemeName()));
        int rows = postMapper.insertTheme(theme);
        if (rows != 1) {
            throw new BaseException(CodeEnum.SYSTEM_ERROR.getCode(), "添加失败");
        }
        redisTemplate.delete(RedisKeyConstant.THEME);
    }

    @Override
    public void deleteTheme(Integer themeId) {
        Boolean hasKey = redisTemplate.opsForHash().hasKey(RedisKeyConstant.THEME, themeId);
        if (!hasKey) {
            throw new BaseException(CodeEnum.PARAM_ERROR.getCode(), "标签不存在错误");
        }
        int rows = postMapper.deleteTheme(themeId);
        if (rows != 1) {
            throw new BaseException(CodeEnum.SYSTEM_ERROR.getCode(), "删除失败");
        }
        redisTemplate.delete(RedisKeyConstant.THEME);
    }

    @Override
    @Transactional
    public void refreshPostLikeNum() {
        Set<String> keys = redisTemplate.keys(RedisKeyConstant.LIKE_NUM + "*");
        List<String> deleteKeys = new ArrayList<>();
        List<Post> posts = new ArrayList<>();
        if (!CollectionUtils.isEmpty(keys)) {
            for (String key : keys) {
                Long expire = redisTemplate.getExpire(key);
                if (expire != null && expire > 0) {
                    Date expireTime = new Date(System.currentTimeMillis() + expire);
                    //如果过期时间小于十分钟，刷新点赞数到mysql中
                    if (expireTime.before(new Date(System.currentTimeMillis() + (10 * 60 * 1000)))) {
                        Integer postId = Integer.valueOf(key.substring(key.lastIndexOf(":") + 1));
                        Integer likeNum = (Integer) redisTemplate.opsForValue().get(key);
                        if (likeNum == null || likeNum == 0) {
                            continue;
                        }
                        Post post = new Post().setId(postId).setLikeNum(likeNum).setUpdateTime(new Date());
                        posts.add(post);
                        deleteKeys.add(key);
                    }
                }
            }
            if (!CollectionUtils.isEmpty(posts)) {
                int rows = postMapper.updatePostLikeNum(posts);
                if (rows != posts.size()) {
                    LOGGER.warn("帖子点赞数更新失败");
                }
                redisTemplate.delete(deleteKeys);
            }
        }
    }

    @Override
    public Map<String, Object> getIndexPost(PostSearchVo postVo, Integer userId) {
        //判断标签是否有问题
        Integer themeId = postVo.getThemeId();
        if (themeId == 0) {
            themeId = null;
        } else {
            Boolean hasKey = redisTemplate.opsForHash().hasKey(RedisKeyConstant.THEME, themeId);
            if (!hasKey) {
                throw new BaseException(CodeEnum.PARAM_ERROR.getCode(), "标签不存在错误");
            }
        }
        Map<String, Object> map = new HashMap<>();
        //查询首页帖子信息并进行分页
        List<IndexPost> postList = postMapper.selectIndexPost(themeId, (postVo.getPage() - 1) * postVo.getRows(), postVo.getRows());
        postList.forEach(indexPost -> {
            //更新帖子信息如：点赞数，是否收藏、点赞等
            updateIndexPost(indexPost, userId);
        });
        Integer totalCount = postMapper.selectPostCount(themeId);
        map.put("indexPosts", postList);
        map.put("totalCount", totalCount);
        return map;
    }


    /**
     * 更新帖子信息
     *
     * @param indexPost
     * @param userId
     */
    private void updateIndexPost(IndexPost indexPost, Integer userId) {
        //更新点赞数量
        Integer likeNum = (Integer) redisTemplate.opsForValue().get(RedisKeyConstant.LIKE_NUM + indexPost.getId());
        if (likeNum == null) {
            redisTemplate.opsForValue().set(RedisKeyConstant.LIKE_NUM + indexPost.getId(), indexPost.getLikeNum(), 7, TimeUnit.DAYS);
        } else {
            indexPost.setLikeNum(likeNum);
        }
        //判断是否为匿名
        if (indexPost.getIsAnonymous()) {
            StuInfo stuInfo = stuInfoService.getStuInfoFromDB(indexPost.getUserInfo().getUserId(), indexPost.getUserInfo().getUserId(), null);
            setAnonymous(indexPost.getUserInfo(), stuInfo.getSex(), indexPost.getIsAnonymous());
        }
        //判断是否点赞了该帖子
        Boolean isLike = redisTemplate.opsForSet().isMember(RedisKeyConstant.LIKE_POST + indexPost.getId(), userId);
        indexPost.setIsLike(isLike);
        //判断是否收藏了该帖子
        Boolean isEnshrine = redisTemplate.opsForSet().isMember(RedisKeyConstant.ENSHRINE + userId, indexPost.getId());
        indexPost.setIsEnshrine(isEnshrine);
    }

    private void setAnonymous(UserInfo userInfo, Boolean sex, Boolean isAnonymous) {
        //判断是否为匿名
        if (isAnonymous) {
            if (sex) {
                if (defaultAvatarIndex++ % 2 == 0) {
                    userInfo.setAvatar(fileUtil.getUrl() + DEFAULT_MAN_AVATAR1).setUsername("匿名");
                } else {
                    userInfo.setAvatar(fileUtil.getUrl() + DEFAULT_MAN_AVATAR2).setUsername("匿名");
                }
            } else {
                if (defaultAvatarIndex++ % 2 == 0) {
                    userInfo.setAvatar(fileUtil.getUrl() + DEFAULT_WOMAN_AVATAR1).setUsername("匿名");
                } else {
                    userInfo.setAvatar(fileUtil.getUrl() + DEFAULT_WOMAN_AVATAR2).setUsername("匿名");
                }
            }
        }
    }

    @Override
    public PostDetail getDetailPost(Integer postId, Integer userId) {
        PostDetail postDetail = new PostDetail();
        //先获取首页帖子信息
        IndexPost indexPost = getIndexPostByPrimaryKey(postId);
        updateIndexPost(indexPost, userId);
        postDetail.setIndexPost(indexPost);
        //再获取评论信息
        List<CommentInfo> commentInfo = commentService.getCommentInfo(postId);
        postDetail.setCommentInfo(commentInfo);
        return postDetail;
    }

    @Override
    public IndexPost getIndexPostByPrimaryKey(Integer postId) {
        IndexPost indexPost = (IndexPost) redisTemplate.opsForValue().get(RedisKeyConstant.INDEX_POST + postId);
        if (indexPost == null) {
            List<IndexPost> indexPosts = postMapper.selectIndexPostByPostVo(new PostSearchVo(postId, null, null));
            if (indexPosts.size() != 1) {
                throw new BaseException(CodeEnum.PARAM_ERROR.getCode(), "帖子id错误");
            }
            indexPost = indexPosts.get(0);
            redisTemplate.opsForValue().set(RedisKeyConstant.INDEX_POST + postId, indexPost, 24, TimeUnit.HOURS);
        }
        return indexPost;
    }

    @Override
    public void likePost(Integer postId, Integer userId) {
        Boolean hasKey = redisTemplate.hasKey(RedisKeyConstant.LIKE_NUM + postId);
        //如果redis中没有该key
        if (!hasKey) {
            IndexPost indexPost = getIndexPostByPrimaryKey(postId);
            redisTemplate.opsForValue().set(RedisKeyConstant.LIKE_NUM + postId, indexPost.getLikeNum(), 24, TimeUnit.HOURS);
        }
        //判断该用户是否已经点赞
        Boolean isMember = redisTemplate.opsForSet().isMember(RedisKeyConstant.LIKE_POST + postId, userId);
        //如果没有点赞
        if (isMember != null && !isMember) {
            redisTemplate.opsForSet().add(RedisKeyConstant.LIKE_POST + postId, userId);
            redisTemplate.opsForValue().increment(RedisKeyConstant.LIKE_NUM + postId);
        } else {
            //如果已经点赞，再次点击就是取消点赞
            redisTemplate.opsForSet().remove(RedisKeyConstant.LIKE_POST + postId, userId);
            redisTemplate.opsForValue().decrement(RedisKeyConstant.LIKE_NUM + postId);
        }
    }


    @Override
    public void enshrinePost(Integer userId, Integer postId) {
        //判断是收藏还是取消收藏
        Boolean exist = redisTemplate.opsForSet().isMember(RedisKeyConstant.ENSHRINE + userId, postId);
        if (exist != null && exist) {
            //如果存在
            redisTemplate.opsForSet().remove(RedisKeyConstant.ENSHRINE + userId, postId);
        } else {
            redisTemplate.opsForSet().add(RedisKeyConstant.ENSHRINE + userId, postId);
        }
    }


    @Override
    public List<IndexPost> getEnshrinePost(Integer userId) {
        //从redis中获取收藏了的帖子id
        Set<Integer> enshrinePostIds = Objects.requireNonNull(redisTemplate.opsForSet().members(RedisKeyConstant.ENSHRINE + userId)).stream().map(o -> (Integer) o).collect(Collectors.toSet());
        if (CollectionUtils.isEmpty(enshrinePostIds)) {
            return null;
        }
        //到数据库中查询
        List<IndexPost> enshrinedPosts = postMapper.selectIndexPostByIds(enshrinePostIds);
        enshrinedPosts.forEach(enshrinedPost -> updateIndexPost(enshrinedPost, userId));

        return enshrinedPosts;
    }

    @Override
    public List<IndexPost> getSelfPost(Integer userId) {
        List<IndexPost> indexPosts = postMapper.selectIndexPostByPostVo(new PostSearchVo(null, null, userId));
        indexPosts.forEach(selfPost -> updateIndexPost(selfPost, userId));
        return indexPosts;
    }

    @Override
    @Transactional
    public void deletePost(Integer postId) {
        //将帖子逻辑删除
        int row = postMapper.deletePostByPrimaryKey(postId);
        if (row != 1) {
            throw new BaseException(CodeEnum.REQUEST_FAILED.getCode(), "删除帖子失败");
        }
        //删除帖子的图片
        List<PostImage> postImages = postMapper.selectPostImages(postId);
        if (postImages != null && postImages.size() > 0) {
            //删除图片
            for (PostImage postImage : postImages) {
                int index = postImage.getImage().lastIndexOf("/");
                try {
                    fileUtil.delete("post/" + postImage.getImage().substring(index + 1));
                } catch (Exception e) {
                    LOGGER.error("七牛云删除图片失败，图片名称为：" + postImage.getImage());
                }
            }
            //将数据库中帖子的图片逻辑删除
            try {
                postMapper.deletePostImagesByPostId(postId);
            } catch (Exception e) {
                LOGGER.warn("数据库帖子图片逻辑删除失败，postId:{}", postId);
            }

        }
        //查找帖子底下的评论
        List<Integer> commentIds = commentService.getCommentIdsByPostId(postId);
        //删除评论
        commentService.deleteComment(postId, commentIds);

        //删除该帖子的举报信息
        postReportService.deletePostReport(0, postId);

    }

}
